package role

import (
	"context"
	"encoding/json"
	"strconv"

	v1 "gitee.com/liangziyisheng/caas-platform-mc/api/role/v1"
	"gitee.com/liangziyisheng/caas-platform-mc/internal/consts"
	"gitee.com/liangziyisheng/caas-platform-mc/internal/dao"
	"gitee.com/liangziyisheng/caas-platform-mc/internal/model/do"
	"gitee.com/liangziyisheng/caas-platform-mc/internal/model/entity"
	"gitee.com/liangziyisheng/caas-platform-mc/internal/service"
	"gitee.com/liangziyisheng/caas-platform-mc/utility/authorization"
	"gitee.com/liangziyisheng/caas-platform-mc/utility/cache"

	"github.com/gogf/gf/v2/errors/gcode"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/glog"
)

type sRole struct{}

var l = g.Log("logger for Role")

func New() *sRole {
	return &sRole{}
}

func init() {
	l.SetFlags(glog.F_FILE_LONG)
	service.RegisterRole(New())
}

func (s *sRole) List(ctx context.Context, in *v1.ListInput) (out *v1.ListOutput, err error) {
	cls := dao.UserRole.Columns()
	md := dao.UserRole.Ctx(ctx)
	if in.AppCode != "" {
		md = md.WhereLike(cls.AppCode, "%"+in.AppCode+"%")
	}
	out = &v1.ListOutput{}
	err = md.Scan(&out.Data)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.ListRoleFailed, in))
	}
	return out, nil
}
func (s *sRole) Info(ctx context.Context, in *v1.InfoInput) (out *v1.InfoOutput, err error) {
	// 查询role 基础信息
	out = &v1.InfoOutput{Permissions: make([]v1.Permission, 0), Users: make([]int64, 0), Menus: make([]entity.RoleMenu, 0)}
	role := &entity.UserRole{}
	rcls := dao.UserRole.Columns()
	err = dao.UserRole.Ctx(ctx).Where(rcls.Id, in.Id).Scan(&role)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.RoleNotExist, in.Id))
	}
	if role.Id == 0 {
		l.Error(ctx, "role not exist", in.Id)
		return nil, gerror.NewCode(consts.RoleNotExist)
	}
	out.Id = role.Id
	out.Name = role.Name
	out.Description = role.Description

	// 查询role关联权限
	e := authorization.NewEnforcer()
	rp, err := e.GetFilteredPolicy(0, role.Name)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.QueryRolePolicyFailed, role.Name))
	}
	// 缓存读取权限信息
	cc, _ := cache.NewCache()
	cache.InitCache(ctx, consts.InitialCacheKeys.PermissionsKey, false)
	val, err := cc.Get(ctx, consts.InitialCacheKeys.PermissionsKey)
	if err != nil {
		return nil, gerror.NewCode(consts.GetCachedPermissionsFailed)
	}
	permissionStr, ok := val.Val().(string)
	if !ok {
		return nil, gerror.NewCode(consts.ParsePermissionCacheFailed)
	}
	permissionMap := make(map[string]entity.Permission, 0)
	json.Unmarshal([]byte(permissionStr), &permissionMap)

	for _, v := range rp {
		p := v1.Permission{}
		p.Id = permissionMap[v[1]+consts.NamedSeperator+v[2]].Id
		p.Operation = permissionMap[v[1]+consts.NamedSeperator+v[2]].Operation
		p.Path = permissionMap[v[1]+consts.NamedSeperator+v[2]].Path
		p.Name = permissionMap[v[1]+consts.NamedSeperator+v[2]].Name
		p.MenuId = permissionMap[v[1]+consts.NamedSeperator+v[2]].MenuId
		out.Permissions = append(out.Permissions, p)
	}

	// 查询role关联的用户
	ru, err := e.GetUsersForRole(role.Name)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.QueryGroupPolicyFailed, role.Name))
	}
	for _, v := range ru {
		userId, err := strconv.ParseInt(v, 10, 64)
		if err != nil {
			l.Error(ctx, err)
			return nil, gerror.NewCode(gcode.WithCode(consts.QueryGroupPolicyFailed, role.Name))
		}
		out.Users = append(out.Users, userId)
	}

	// 查询role关联的菜单
	err = dao.RoleMenu.Ctx(ctx).Where(dao.RoleMenu.Columns().RoleName, role.Name).Scan(&out.Menus)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCodef(consts.QueryRoleMenuFailed, consts.QueryRoleMenuFailed.Message(), role.Id)
	}
	return out, nil
}

func (s *sRole) Add(ctx context.Context, in *v1.AddInput) (out *v1.AddOutput, err error) {
	id, err := dao.UserRole.Ctx(ctx).Data(in.UserRole).InsertAndGetId()
	if err != nil {
		return nil, gerror.NewCode(gcode.WithCode(consts.AddRoleFailed, in.UserRole))
	}
	out = &v1.AddOutput{Id: id}
	return out, nil
}

func (s *sRole) Delete(ctx context.Context, in *v1.DeleteInput) (out *v1.DeleteOutput, err error) {

	// 查询role详情
	role := entity.UserRole{}
	rcls := dao.UserRole.Columns()
	err = dao.UserRole.Ctx(ctx).Scan(&role)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.RoleNotExist, in.Id))
	}
	if role.Id == 0 {
		l.Error(ctx, "role not exist", in.Id)
		return nil, gerror.NewCode(consts.RoleNotExist)
	}

	// 检查role中是否包含了user
	e := authorization.NewEnforcer()
	ru, err := e.GetFilteredGroupingPolicy(1, role.Name)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.QueryGroupPolicyFailed, role.Name))
	}
	if len(ru) > 0 {
		return nil, gerror.NewCode(consts.RoleAssignedByUser)
	}
	// 检查role中是否包含permission
	rp, err := e.GetFilteredPolicy(0, role.Name)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.QueryPolicyFailed, role.Id))
	}
	if len(rp) > 0 {
		return nil, gerror.NewCode(consts.RoleIncludingPermission)
	}

	// 最后删除
	rs, err := dao.UserRole.Ctx(ctx).Where(rcls.Id, in.Id).Delete()
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.DeleteRoleFailed, in.Id))
	}
	out = &v1.DeleteOutput{}
	if out.Rows, err = rs.RowsAffected(); err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.DeleteRoleFailed, in.Id))
	}
	if out.Rows == 0 {
		return nil, gerror.NewCode(gcode.WithCode(consts.DeleteFailed, in.Id))
	}
	return out, nil
}

func (s *sRole) Update(ctx context.Context, in *v1.UpdateInput) (out *v1.UpdateOutput, err error) {
	cls := dao.UserRole.Columns()
	out = &v1.UpdateOutput{}
	out.Rows, err = dao.UserRole.Ctx(ctx).Data(do.UserRole{Description: in.Description}).Where(cls.Id, in.Id).UpdateAndGetAffected()
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.UpdateRoleFailed, in))
	}
	if out.Rows == 0 {
		return nil, gerror.NewCode(gcode.WithCode(consts.UpdateFailed, in.Id))
	}
	return out, nil
}

func (s *sRole) AssignPermission(ctx context.Context, in *v1.AssignPermissionInput) (out *v1.AssignPermissionOutput, err error) {
	// 检查role存在
	role := entity.UserRole{}
	rcls := dao.UserRole.Columns()
	err = dao.UserRole.Ctx(ctx).Where(rcls.Id, in.Id).Scan(&role)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.RoleNotExist, in.Id))
	}
	if role.Id == 0 {
		l.Error(ctx, "role not exist", in.Id)
		return nil, gerror.NewCode(gcode.WithCode(consts.RoleNotExist, in.Id))
	}
	// if len(in.PermissionsId) == 0 {
	// 	return &v1.AssignPermissionOutput{Rows: 0}, nil
	// }
	// 查询数据库确保存在并Assign
	// 启动事务
	tx, err := g.DB().Begin(ctx)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.AssignPermissionToRoleFailed, in.Id))
	}
	if in.Include {
		// 更新角色菜单
		_, err = tx.Model(dao.RoleMenu.Table()).Data(do.RoleMenu{RoleName: role.Name, MenuId: in.MenuId}).Save()
		if err != nil {
			l.Error(ctx, err)
			tx.Rollback()
			return nil, gerror.NewCode(consts.UpdateRoleMenuFailed)
		}
	} else {
		rmcls := dao.RoleMenu.Columns()
		_, err = tx.Model(dao.RoleMenu.Table()).Where(rmcls.RoleName, role.Name).Where(rmcls.MenuId, in.MenuId).Delete()
		if err != nil {
			l.Error(ctx, err)
			tx.Rollback()
			return nil, gerror.NewCode(consts.UpdateRoleMenuFailed)
		}
	}
	AssignPermissions := make([]entity.Permission, 0)
	if len(in.PermissionsId) > 0 {
		// 开始插入casbin
		pcls := dao.Permission.Columns()
		err = dao.Permission.Ctx(ctx).Where(pcls.MenuId, in.MenuId).WhereIn(pcls.Id, in.PermissionsId).Scan(&AssignPermissions)
		if err != nil {
			l.Error(ctx, err)
			tx.Rollback()
			return nil, gerror.NewCode(gcode.WithCode(consts.QueryAppPermissionFailed, in.PermissionsId))
		}
		// 构建rules
		rules := make([][]string, 0)
		for _, ap := range AssignPermissions {
			rules = append(rules, []string{role.Name, ap.Path, ap.Operation})
		}
		e := authorization.NewEnforcer()
		_, err = e.AddPolicies(rules)
		if err != nil {
			l.Error(ctx, err)
			tx.Rollback()
			return nil, gerror.NewCode(consts.AssignPermissionToRoleFailed)
		}
	}
	tx.Commit()
	return &v1.AssignPermissionOutput{Rows: len(AssignPermissions)}, nil
}

func (s *sRole) UnassignPermission(ctx context.Context, in *v1.UnassignPermissionInput) (out *v1.UnassignPermissionOutput, err error) {
	// 检查role存在
	role := entity.UserRole{}
	rcls := dao.UserRole.Columns()
	err = dao.UserRole.Ctx(ctx).Where(rcls.Id, in.Id).Scan(&role)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.QueryRoleFailed, in.Id))
	}
	if role.Id == 0 {
		l.Error(ctx, "role not exist", in.Id)
		return nil, gerror.NewCode(gcode.WithCode(consts.RoleNotExist, in.Id))
	}

	tx, err := g.DB().Begin(ctx)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.AssignPermissionToRoleFailed, in.Id))
	}
	if in.Include {
		// 更新角色菜单
		_, err = tx.Model(dao.RoleMenu.Table()).Data(do.RoleMenu{RoleName: role.Name, MenuId: in.MenuId}).Save()
		if err != nil {
			l.Error(ctx, err)
			tx.Rollback()
			return nil, gerror.NewCode(consts.UpdateRoleMenuFailed)
		}
	} else {
		rmcls := dao.RoleMenu.Columns()
		_, err = tx.Model(dao.RoleMenu.Table()).Where(rmcls.RoleName, role.Name).Where(rmcls.MenuId, in.MenuId).Delete()
		if err != nil {
			l.Error(ctx, err)
			tx.Rollback()
			return nil, gerror.NewCode(consts.UpdateRoleMenuFailed)
		}
	}
	UnassignPermissions := make([]entity.Permission, 0)
	if len(in.PermissionsId) > 0 {
		// 更新casbin
		pcls := dao.Permission.Columns()
		err = dao.Permission.Ctx(ctx).Where(pcls.MenuId, in.MenuId).WhereIn(pcls.Id, in.PermissionsId).Scan(&UnassignPermissions)
		if err != nil {
			tx.Rollback()
			l.Error(ctx, err)
			return nil, gerror.NewCode(gcode.WithCode(consts.QueryAppPermissionFailed, in.PermissionsId))
		}
		// 构建rules
		rules := make([][]string, 0)
		for _, ap := range UnassignPermissions {
			rules = append(rules, []string{role.Name, ap.Path, ap.Operation})
		}
		e := authorization.NewEnforcer()
		_, err = e.RemovePolicies(rules)
		if err != nil {
			l.Error(ctx, err)
			tx.Rollback()
			return nil, gerror.NewCode(consts.UnassignPermissionToRoleFailed)
		}
	}
	tx.Commit()
	return &v1.UnassignPermissionOutput{Rows: len(UnassignPermissions)}, nil
}

func (s *sRole) AssignUser(ctx context.Context, in *v1.AssignUserInput) (out *v1.AssignUserOutput, err error) {
	// 检查role存在
	role := entity.UserRole{}
	rcls := dao.UserRole.Columns()
	err = dao.UserRole.Ctx(ctx).Where(rcls.Id, in.Id).Scan(&role)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.AssignUserToRoleFailed, in.Id))
	}
	if role.Id == 0 {
		return nil, gerror.NewCode(consts.RoleNotExist)
	}
	if len(in.UsersId) == 0 {
		return &v1.AssignUserOutput{Rows: 0}, nil
	}
	// 查询数据库确保存在并Assign
	AssignUsers := make([]entity.User, 0)
	ucls := dao.User.Columns()
	err = dao.User.Ctx(ctx).WhereIn(ucls.Id, in.UsersId).Where(ucls.TenantCode, in.TenantCode).Scan(&AssignUsers)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.QueryUserFailed, in.UsersId))
	}
	for _, au := range AssignUsers {
		e := authorization.NewEnforcer()
		_, err = e.AddGroupingPolicy(strconv.FormatInt(au.Id, 10), role.Name)
		if err != nil {
			l.Error(ctx, err)
			return nil, gerror.NewCode(gcode.WithCode(consts.AddGroupPolicyFailed, strconv.FormatInt(au.Id, 10)+consts.NamedSeperator+role.Name))
		}
	}
	return &v1.AssignUserOutput{Rows: len(AssignUsers)}, nil
}

func (s *sRole) UnassignUser(ctx context.Context, in *v1.UnassignUserInput) (out *v1.UnassignUserOutput, err error) {
	// 检查role存在
	role := entity.UserRole{}
	rcls := dao.UserRole.Columns()
	err = dao.UserRole.Ctx(ctx).Where(rcls.Id, in.Id).Scan(&role)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.QueryRoleFailed, in.Id))
	}
	if role.Id == 0 {
		l.Error(ctx, "role not exist", in.Id)
		return nil, gerror.NewCode(gcode.WithCode(consts.RoleNotExist, in.Id))
	}
	if len(in.UsersId) == 0 {
		return &v1.UnassignUserOutput{Rows: 0}, nil
	}
	// 查询数据库确保存在并Assign
	UnssignUsers := make([]entity.User, 0)
	ucls := dao.User.Columns()
	err = dao.User.Ctx(ctx).WhereIn(ucls.Id, in.UsersId).Where(ucls.TenantCode, in.TenantCode).Scan(&UnssignUsers)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.QueryUserFailed, in.UsersId))
	}
	for _, au := range UnssignUsers {
		e := authorization.NewEnforcer()
		_, err = e.RemoveGroupingPolicy(strconv.FormatInt(au.Id, 10), role.Name)
		if err != nil {
			l.Error(ctx, err)
			return nil, gerror.NewCode(gcode.WithCode(consts.UnassignUserToRoleFailed, strconv.FormatInt(au.Id, 10)+consts.NamedSeperator+role.Name))
		}
	}

	return &v1.UnassignUserOutput{Rows: len(UnssignUsers)}, nil
}
